﻿namespace Base64Url
{
    using System;

    public static class Base64Url
    {
        /// <summary>
        /// Converts the value of an array of 8-bit unsigned integers to its equivalent String representation encoded with base 64 url digits.
        /// </summary>
        /// <param name="inArray">An array of 8-bit unsigned integers.</param>
        /// <returns>The String representation, in base 64 url, of the contents of inArray.</returns>
        /// <exception cref="System.ArgumentNullException">inArray is a null reference.</exception>
        public static string ToBase64UrlString(byte[] inArray)
        {
            if (inArray == null)
            {
                throw new ArgumentNullException("inArray", "inArray is a null reference.");
            }
            else
            {
                return ToBase64UrlString(inArray, 0, inArray.Length, Base64FormattingOptions.None);
            }
        }

        /// <summary>
        /// Converts a subset of an array of 8-bit unsigned integers to its equivalent String representation encoded with base 64 url digits. A parameter specifies whether to insert line breaks in the return value.
        /// </summary>
        /// <param name="inArray">An array of 8-bit unsigned integers.</param>
        /// <param name="options">InsertLineBreaks to insert a line break every 76 characters, or None to not insert line breaks.</param>
        /// <returns>The String representation in base 64 url of the elements in inArray.</returns>
        /// <exception cref="System.ArgumentNullException">inArray is a null reference.</exception>
        public static string ToBase64UrlString(byte[] inArray, Base64FormattingOptions options)
        {
            if (inArray == null)
            {
                throw new ArgumentNullException("inArray", "inArray is a null reference.");
            }
            else
            {
                return ToBase64UrlString(inArray, 0, inArray.Length, options);
            }
        }

        /// <summary>
        /// Converts a subset of an array of 8-bit unsigned integers to its equivalent String representation encoded with base 64 url digits. Parameters specify the subset as an offset in the input array, and the number of elements in the array to convert.
        /// </summary>
        /// <param name="inArray">An array of 8-bit unsigned integers.</param>
        /// <param name="offset">An offset in inArray.</param>
        /// <param name="length">The number of elements of inArray to convert.</param>
        /// <returns>The String representation in base 64 url of length elements of inArray starting at position offset.</returns>
        /// <exception cref="System.ArgumentNullException">inArray is a null reference.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">offset or length is negative. or offset plus length is greater than the length of inArray.</exception>
        public static string ToBase64UrlString(byte[] inArray, int offset, int length)
        {
            if (inArray == null)
            {
                throw new ArgumentNullException("inArray", "inArray is a null reference.");
            }
            else if (offset < 0)
            {
                throw new ArgumentOutOfRangeException("offset", "offset is negative.");
            }
            else if (length < 0)
            {
                throw new ArgumentOutOfRangeException("length", "length is negative.");
            }
            else if (offset + length > inArray.Length)
            {
                throw new ArgumentOutOfRangeException("length", "offset plus length is greater than the length of inArray.");
            }
            else
            {
                return ToBase64UrlString(inArray, offset, length, Base64FormattingOptions.None);
            }
        }

        /// <summary>
        /// Converts a subset of an array of 8-bit unsigned integers to its equivalent String representation encoded with base 64 url digits. Parameters specify the subset as an offset in the input array, the number of elements in the array to convert, and whether to insert line breaks in the return value.
        /// </summary>
        /// <param name="inArray">An array of 8-bit unsigned integers. </param>
        /// <param name="offset">An offset in inArray.</param>
        /// <param name="length">The number of elements of inArray to convert.</param>
        /// <param name="options">InsertLineBreaks to insert a line break every 76 characters, or None to not insert line breaks.</param>
        /// <returns>The String representation in base 64 url of length elements of inArray starting at position offset.</returns>
        /// <exception cref="System.ArgumentNullException">inArray is a null reference.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">offset or length is negative. or offset plus length is greater than the length of inArray.</exception>
        public static string ToBase64UrlString(byte[] inArray, int offset, int length, Base64FormattingOptions options)
        {
            if (inArray == null)
            {
                throw new ArgumentNullException("inArray", "inArray is a null reference.");
            }
            else if (offset < 0)
            {
                throw new ArgumentOutOfRangeException("offset", "offset is negative.");
            }
            else if (length < 0)
            {
                throw new ArgumentOutOfRangeException("length", "length is negative.");
            }
            else if (offset + length > inArray.Length)
            {
                throw new ArgumentOutOfRangeException("length", "offset plus length is greater than the length of inArray.");
            }
            else
            {
                return Convert.ToBase64String(inArray, offset, length, options).TrimEnd('=').Replace('+', '-').Replace('/', '_');
            }
        }

        /// <summary>
        /// Converts the specified String, which encodes binary data as base 64 digits, to an equivalent 8-bit unsigned integer array.
        /// </summary>
        /// <param name="s">A String.</param>
        /// <returns>An array of 8-bit unsigned integers equivalent to s. </returns>
        /// <exception cref="System.ArgumentNullException">s is a null reference.</exception>
        /// <exception cref="System.FormatException">The format of s is invalid. s contains a non-base 64 character, more than two padding characters, or a non-white space character among the padding characters.</exception>
        public static byte[] FromBase64UrlString(string s)
        {
            if (s == null)
            {
                throw new ArgumentNullException("s");
            }
            else
            {
                int length = s.Length % 4;
                int padLength = length == 0 ? 0 : 4 - length;
                string base64url = s.Replace('-', '+').Replace('_', '/') + new string('=', padLength);

                try
                {
                    return Convert.FromBase64String(base64url);
                }
                catch (FormatException formatException)
                {
                    throw new FormatException(formatException.Message, formatException);
                }
            }
        }
    }
}